// file:system/time/clock.c
// autor:jiangxinpeng
// time:2021.6.24
// copyright:(C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/clock.h>
#include <os/softirq.h>
#include <os/hardirq.h>
#include <os/schedule.h>
#include <os/walltime.h>
#include <os/task.h>
#include <arch/tss.h>
#include <os/alarm.h>
#include <os/timer.h>
#include <os/debug.h>
#include <arch/time.h>
#include <arch/cpu.h>
#include <arch/config.h>
#include <arch/apic.h>
#include <lib/type.h>

#pragma GCC optimize("O0")

volatile clock_t sys_ticks = 0;
volatile clock_t timer_ticks = 0;

// use to PIT timer
static void TimerSoftIrqHandler()
{
    if (!((uint32_t)sys_ticks % HZ))
    {
        // update walltime and pass one second
        WallTimeUpdateSecond();
    }
    // update timer ticks
    TimerUpdateTicks();
    // update alram ticks
    AlarmUpdateTicks();
}

static void SchedSoftIrqHandler()
{
    task_t *cur = cur_task;
    if (cur->stack_magic != TASK_STACK_MAGIC)
    {
        Panic(PRINT_ERR "task stack magic no true\n");
    }
    // task had run ticks
    cur->elapsed_ticks++;

    // task ticks end,schedule
    if (cur->ticks <= 0)
    {
        sched_flags=1;
    }
    else
    {
        // task ticks--
        cur->ticks--;
    }
}

static int ClockHandler(irqno_t irq, void *data)
{
    // update sys ticks
    sys_ticks++;
    // update timer ticks
    timer_ticks++;
#ifndef ENABLE_SMP
    SoftirqActive(SCHED_SOFTIRQ);
#endif
    SoftirqActive(TIMER_SOFTIRQ);
    return 0;
}

void ClockInit()
{
    // softirq register
    SoftirqBuild(TIMER_SOFTIRQ, TimerSoftIrqHandler);
    SoftirqBuild(SCHED_SOFTIRQ, SchedSoftIrqHandler);

    timer_ticks = sys_ticks = 0;
    ClockHardwareInit();
#ifdef ENABLE_SMP
    SMPTimerHardwareInit();
#endif

    // register clock hardware irq
    if (IrqRegister(IRQ0_CLOCK, ClockHandler, IRQ_DISABLE, "irq0", "kclock", NULL))
    {
        KPrint(PRINT_ERR "register clock irq failed\n", IRQ0_CLOCK);
        return;
    }
    KPrint("[clock] hardward init done\n");
    return;
}

void Mdelay(time_t msec)
{
    clock_t ticks = MSEC_TO_TICKS(msec);
    clock_t start = sys_ticks;
    if (!ticks)
        ticks = 1;
    while (SysGetTicks() - start < ticks)
        CpuDoSleep();
}

clock_t ClockDelayByTicks(clock_t ticks)
{
    clock_t start = sys_ticks;
    while (sys_ticks - start < ticks)
    {
        TaskYield();
    }
    return ticks;
}

clock_t SysGetTicks()
{
    return sys_ticks;
}
